<?
// CyBerFuN.ro & xList.ro

/////////////////////////////////////////////////////////////////////////////////////
// xbtit - Bittorrent tracker/frontend
//
// Copyright (C) 2004 - 2007  Btiteam
//
//    This file is part of xbtit.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   1. Redistributions of source code must retain the above copyright notice,
//      this list of conditions and the following disclaimer.
//   2. Redistributions in binary form must reproduce the above copyright notice,
//      this list of conditions and the following disclaimer in the documentation
//      and/or other materials provided with the distribution.
//   3. The name of the author may not be used to endorse or promote products
//      derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
////////////////////////////////////////////////////////////////////////////////////
class update_hacks
      {

      var $file = array();
      var $errors = array();
      var $hack_path;
      var $errors_count;
      var $ftp_server;
      var $ftp_port;
      var $ftp_username;
      var $ftp_password;
      var $ftp_basedir;
      var $rftp;
      var $ftp_files_to_copy = array();
      var $ftp_login_need;
      var $ftp_files_to_chmod = array();
      var $files_to_backup = array();

      function update_hacks()
            {
            
            include(dirname(__FILE__)."/class.archive.php");

            // reset all var
            $this->file = array();
            $this->errors = array();
            $this->hack_path = "";
            $this->errors_count = 0;
            unset($this->ftp_server);
            unset($this->ftp_port);
            unset($this->ftp_username);
            unset($this->ftp_password);
            unset($this->ftp_basedir);
            $this->rftp = null;
            $this->ftp_files_to_copy = array();
            $this->ftp_login_need = false;
            $this->ftp_files_to_chmod = array();
            $this->files_to_backup = array();
      }

      // private
      function _err_message($e_message, $e_file, $e_solution)
          {
          $this->errors[$this->errors_count]["message"] = $e_message;
          $this->errors[$this->errors_count]["file"] = $e_file;
          $this->errors[$this->errors_count]["solution"] = $e_solution;
          $this->errors_count++;
       }

      function ftp_open()
        {
          if (!isset($this->ftp_server) || !isset($this->ftp_port) || !isset($this->ftp_username) || !isset($this->ftp_password))
             {
             session_unset();
             session_destroy();
             $_SESSION = array();
             $this->_err_message("Missed FTP data!","FTP","Correct FTP server, port");
             return false;
          }

          $this->rftp = @ftp_connect($this->ftp_server, $this->ftp_port, 30);
          if (!$this->rftp)
             {
             session_unset();
             session_destroy();
             $_SESSION = array();
             $this->_err_message("Ftp Connection Failed!", "FTP", "Correct FTP server, port");
             return false;
          }
          $uftp = @ftp_login($this->rftp, $this->ftp_username, $this->ftp_password);
          if (!$uftp)
             {
             session_unset();
             session_destroy();
             $_SESSION = array();
             $this->_err_message("Ftp Login  Failed!", "FTP", "Correct credentials");
             @ftp_close($this->rftp);
             return false;
          }
          @ftp_pasv($this->rftp, true);
          return true; // connected
      }

      function ask_for_ftp_form()
         {
         global $CURUSER;
         if (isset($_POST["add_hack_folder"]))
             $hack_folder = $_POST["add_hack_folder"];
         header("Location: index.php?page=admin&user=".$CURUSER["uid"]."&code=".$CURUSER["random"]."&do=hacks&action=ftp&hack=".urlencode($hack_folder));
         die;

      }


      function ftp_session_begin()
         {
             if ($this->ftp_login_need == false)
                return true;

             if (!$_SESSION["ftp_data"])
                $this->ask_for_ftp_form();

             $this->ftp_server = $_SESSION["ftp_data"]["server"];
             $this->ftp_port = $_SESSION["ftp_data"]["port"];
             $this->ftp_username = $_SESSION["ftp_data"]["username"];
             $this->ftp_password = $_SESSION["ftp_data"]["pass"];
             $this->ftp_basedir = $_SESSION["ftp_data"]["basedir"];

             if ($this->ftp_basedir == "/" || $this->ftp_basedir == "./")
                $this->ftp_basedir = "";

             return $this->ftp_open();
      }

      function ftp_session_end()
         {
             if ($this->ftp_login_need == false)
                return true;

             if (!$_SESSION["ftp_data"])
                return false;

             return @ftp_close($this->rftp);
      }


      function convert_real_to_relative($realfile)
        {

            global $THIS_BASEPATH;
            //if (strpos($realfile,":") !== false)
            //   $realfile = substr($realfile,strpos($realfile,":")+1);

            $realfile = (str_replace("\\","/", $realfile));

            $realfile = str_replace(str_replace("\\","/",$THIS_BASEPATH)."/", $this->ftp_basedir, $realfile);

            return $realfile;

      }


      function ftp_fchmod($f, $val)
        {

          // chmod is ONLY for *nix server
          $os = (strpos(strtolower(PHP_OS),"win") === false);
          if (!$os)
            return true;

          if (is_array($f))
             {
             foreach($f as $nf)
               {

                 if (ftp_site($this->rftp, sprintf("chmod %u %s",$val, $nf)) === false) // chmod faild
                    {
                    $this->_err_message("Ftp \"chmod $val\" Failed!",$nf,"Manually chmod to $val");
                    return false;
                 }
            }
          }
          else
            {
            if (ftp_site($this->rftp,sprintf("chmod %u %s",$val, $f)) === false) // chmod faild
               {
               $this->_err_message("Ftp \"chmod $val\" Failed!", $f, "Manually chmod to $val");
               return false;
            }
          }
          return true;
      }

      function ftp_fdelete($files)
        {
            if (empty($files))
               return;

            if (is_array($files))
             {
             foreach ($files as $f)
               {
                 $this->ftp_fdelete($f["newpath"]."/".$f["newfile"]);
                 return;
             }
            }

          if (!file_exists($files))
             {
             if (is_array($files))
                $this->_err_message("Ftp deleting file Failed!", $files, "don't exists!");
                return false;
          }


          if (!ftp_delete($this->rftp, $this->convert_real_to_relative($files)))
             {
             $this->_err_message("Ftp deleting file Failed!", $files, "$files is readable?");
             return false;
          }

          return true;
      }

      function ftp_copy($ori_file, $dest_file)
        {
          if (!file_exists($ori_file))
             {
             $this->_err_message("Ftp copying file Failed!", $dest_file, "$ori_file exists?");
             return false;
          }
          // need to call open before...
          $fpo = @fopen($ori_file, "r");
          if (!$fpo)
             {
             $this->_err_message("Ftp copying file Failed (opening)!", $dest_file, "$ori_file is readable?");
             return false;
          }
          
          if (in_array(substr($ori_file, -3), array("tpl","php","txt")))
             $ftp_mode = FTP_ASCII;
          else
             $ftp_mode = FTP_BINARY;


          if (!ftp_fput($this->rftp, $this->convert_real_to_relative($dest_file), $fpo,$ftp_mode))
             {
             $this->_err_message("Ftp copying file Failed!", $dest_file, "$ori_file is readable?");
             fclose($fpo);
             //return false;
          }
          fclose($fpo);
          // try to chmod file to 0777
          $response = @$this->ftp_fchmod($this->convert_real_to_relative($dest_file), "777");

          if (!$response) // chmod faild
             {
             $this->_err_message("Ftp chmod file Failed!", $dest_file, "$ori_file");
             return false;
          }
          else
             return true;
      }


      function ftp_make_new_folder($folder_name)
        {
          if (is_array($folder_name))
             {
             foreach($folder_name as $newfolder)
               {
                 if (!@ftp_mkdir($this->rftp, $this->convert_real_to_relative($newfolder)))
                    return false;
                 if (!$this->ftp_fchmod($this->convert_real_to_relative($newfolder), "777"))
                    return false;
            }
          }
          else
              {
                if (!@ftp_mkdir($this->rftp, $this->convert_real_to_relative($folder_name)))
                   return false;
                 if (!$this->ftp_fchmod($this->convert_real_to_relative($folder_name),"777"))
                    return false;
           }
          return true;
      }

      // open the xml input file and return the full stream
      function open_hack($filename_and_folder)
            {
            // will open the main file which will contain the informations for insert hack.
            if (!file_exists($filename_and_folder))
                {
                //$this->errors[]["message"]="File \"$filename_and_folder\" seems to be missed!";
                $this->_err_message("File seems to be missed!", $filename_and_folder, "Check Hack's Folder");
                return false;
             }

            if (!is_readable($filename_and_folder))
                {
                //$this->errors[]["message"]="File \"$filename_and_folder\" seems to be not readable";
                $this->_err_message("File seems to be not readable!", $filename_and_folder, "Check chmod+chown for $filename_and_folder");
                return false;
             }

             $fp = @fopen($filename_and_folder,"r");

             if (!$fp)
                {
                //$this->errors[]["message"]="Error reading file \"$filename_and_folder\"";
                $this->_err_message("Error reading file", $filename_and_folder, "Check chmod+chown for $filename_and_folder");
                return false;
             }

             $full_file = "";
             while (!feof($fp))
                  $full_file .= fread($fp, 4096);

             $this->hack_path = dirname($filename_and_folder);
            
             return $full_file;
      }


      // private
      // used by hack_to_array, find the content in $text between <$tag> and </$tag>
      function get_tag_value($text, $tag) {
          $StartTag = "<$tag";
          $EndTag = "</$tag";
          
          $StartPosTemp = strpos($text, $StartTag);
          $StartPos = strpos($text, '>', $StartPosTemp);
          $StartPos = $StartPos + 1;
          
          $EndPos = strpos($text, $EndTag);
          
          if($EndPos > $StartPos) {
              $text   = substr ($text, $StartPos, ($EndPos - $StartPos));
          } else {
              $text = '';
          }

          $StartPos = strpos($text, '<![CDATA[');
          $EndPos = strpos($text, ']]>');
          if ($EndPos > $StartPos)
             $text = substr ($text, $StartPos, ($EndPos - $StartPos));

          $text = str_replace('<![CDATA[', '', $text);
          $text = str_replace(']]>', '', $text);

          return $text;
      }

      // read xml type stream and create valid array for applying hack
      // Array
      // (
      //    [0] => Array
      //        (
      //            [title]   => title of the hack
      //            [version] => version number of the hack
      //            [author]  => author's name
      //            [file]    => Array
      //                (
      //                    [0] => Array
      //                        (
      //                            [name] => full file path to modify
      //                            [operations] => Array
      //                                (
      //                                    [0] => Array
      //                                        (
      //                                            [search] => text to search (optional if copy)
      //                                            [action] => add/remove/copy/sql
      //                                            [data]   => text to insert or replace or file name (for copy)
      //                                            [where] => after/before/path if copy (optional if remove)
      //                                        )
      //                                )
      //                        )
      //                )
      //        )
      // )
      function hack_to_array($string) {
          $hacks = explode ('<hack>', $string);
          $i = 0;
          $hack = array();
          array_shift($hacks);
          foreach($hacks as $h)
            {
              $hack[$i]['title'] = $this->get_tag_value($h, 'title');
              $hack[$i]['version'] = $this->get_tag_value($h, 'version');
              $hack[$i]['author'] = $this->get_tag_value($h, 'author');
              // split all files on this hack
              $hack_file = explode('<file>', $h);
              array_shift($hack_file);
              $x = 0;
              foreach($hack_file as $hf)
                 {
                   $hack[$i]['file'][$x]['name'] = $this->get_tag_value($hf, 'name');
                   // split all operations on this file
                   $hack_operations = explode('<operation>', $hf);
                   array_shift($hack_operations);
                   $j = 0;
                   foreach($hack_operations as $op)
                     {
                        $hack[$i]['file'][$x]['operations'][$j]['search'] = $this->get_tag_value($op, 'search');
                        $hack[$i]['file'][$x]['operations'][$j]['action'] = $this->get_tag_value($op, 'action');
                        $hack[$i]['file'][$x]['operations'][$j]['data'] = $this->get_tag_value($op, 'data');
                        $hack[$i]['file'][$x]['operations'][$j]['where'] = $this->get_tag_value($op, 'where');
                        $j++;
                   }
                   $x++;
              }
              $i++;
          }
          unset($hacks);
          return $hack;
      }

      // private:
      // try to open input file and if success return file's stream
      function open_read_file($file_to_hack, $ind)
         {
            // globals var
            global $THIS_BASEPATH, $CURRENT_FOLDER;

            $DEFAULT_ROOT = $THIS_BASEPATH;
            $DEFAULT_STYLE_PATH = "$THIS_BASEPATH/style/xbtit_default";
            $DEFAULT_LANGUAGE_PATH = "$THIS_BASEPATH/language/english";

         // file exists?
           if (!file_exists($file_to_hack))
             {
               //$this->errors[]["message"]="File $file_to_hack don't exists!";
               $this->_err_message("File don't exists!", $file_to_hack, "Control if file exists");
               return false;
           }
           // i can read it?
           if (!is_readable($file_to_hack))
             {
              // try to make it readable...
              $ok = @chmod($file_to_hack, 0755);
              if (!is_readable($file_to_hack))
                {
                 $this->ftp_login_need = true;
                 $index=count($this->ftp_files_to_chmod);
                 $this->ftp_files_to_chmod[$index]["index"] = $ind;
                 $this->ftp_files_to_chmod[$index]["file"] = $file_to_hack;
                 $this->ftp_files_to_chmod[$index]["mode"] = "744";
                 $ok = false;
              }
           }
           else $ok = true;

           // all done return file resource id
           if ($ok)
             {
             $stream = "";
             $fp = fopen($file_to_hack, "r");
             if (!$fp)
               {
               //$this->errors[]["message"]="Error opening File ($file_to_hack)!";
               $this->_err_message("Error opening file!", $file_to_hack, "Check chmod+chown for $file_to_hack");
               return false;
             }
             else
               {
                 while(!feof($fp))
                      $stream .= fread($fp, 4096);
                 fclose($fp);
             }
             return $stream;
           }
           else
             {
             //$this->errors[]["message"]="File $file_to_hack is not readable!";
             //$this->_err_message("File seems to be not readable!",$file_to_hack,"Check chmod+chown for $file_to_hack");
             //return false;
             // we will try now using ftp...
           }

      }


      // private, used only to set errors in sql action when it fail
      function db_error(){
        global $j;

        //$this->errors[]["message"]=mysql_error();
        $this->_err_message(mysql_error(), "Sql", "Check the query");
        $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:red;\">Failed</span>";
        $this->file[$j]["operation"] = "Sql";

      }

      function chmod_ftp_files()
        {
          if (count($this->ftp_files_to_chmod) == 0)
             return true;

          foreach ($this->ftp_files_to_chmod as $ftc)
            {
              $file = $ftc["file"];
              $mode = $ftc["mode"];
              $index = $ftc["index"];
              if (!file_exists($file))
                {
                  $this->_err_message("Error: file don't exists!", $file, "Check it");
                  $this->file[$index]["status"] = "<span style=\"font-weight: bold; color:red;\">Failed</span>";
                  return false;
              }
              if (!$this->ftp_fchmod($this->convert_real_to_relative($file), $mode))
                {
                 //$this->errors[]["message"] = "Error: $new_file_path is not writable";
                 //$this->_err_message("Error: file is not writable!", $file, "Manually set permissions (766 is good)");
                 $this->file[$index]["status"] = "<span style=\"font-weight: bold; color:red;\">Failed</span>";
                 return false;
              }
              else
                 $this->file[$index]["status"] = "<span style=\"font-weight: bold; color:green;\">OK</span>";
          }
          return true;
      }

      function copy_ftp_files()
        {
          if (count($this->ftp_files_to_copy) == 0)
             return true;

          foreach ($this->ftp_files_to_copy as $ftc)
            {
              $original_file = $ftc["origine"];
              if (substr($ftc["newpath"],strlen($ftc["newpath"])-1, 1) == "/")
                $new_file_path = substr($ftc["newpath"], 0, strlen($ftc["newpath"])-1);
              else
                $new_file_path = $ftc["newpath"];
              $new_file_name = $ftc["newfile"];
              $index = $ftc["index"];

              if (!file_exists($new_file_path))
                {
                  if (!$this->ftp_make_new_folder($this->convert_real_to_relative($new_file_path)))
                    {
                     //$this->errors[]["message"] = "Error: $new_file_path was not created";
                     $this->_err_message("Error: file was not created in $new_file_path!", $new_file_name, "Check $new_file_path Permission (766 is good)");
                     $this->file[$index]["status"] = "<span style=\"font-weight: bold; color:red;\">Failed</span>";
                     return false;
                  }
                  else
                     $this->file[$index]["status"] = "<span style=\"font-weight: bold; color:green;\">OK</span>";
              }

              if (!is_writable($new_file_path))
                {
                  if (!$this->ftp_fchmod($this->convert_real_to_relative($new_file_path), "777"))
                    {
                     //$this->errors[]["message"] = "Error: $new_file_path is not writable";
                     //$this->_err_message("Error: file is not writable!", $new_file_name, "Check $new_file_path Permission (766 is good)");
                     $this->file[$index]["status"] = "<span style=\"font-weight: bold; color:red;\">Failed</span>";
                     return false;
                  }
                  else
                     $this->file[$index]["status"] = "<span style=\"font-weight: bold; color:green;\">OK</span>";

                  $this->ftp_fchmod($this->convert_real_to_relative($new_file_path), "755");
              }

              if (!$this->ftp_copy($original_file, $new_file_path."/".$new_file_name))
                {
                   $this->_err_message("Can't copy file!",$new_file_name, "Check $new_file_path Permission (766 is good)");
                   $this->file[$index]["status"] = "<span style=\"font-weight: bold; color:red;\">Failed</span>";
                   return false;
              }

              $this->file[$index]["status"] = "<span style=\"font-weight: bold; color:green;\">OK</span>";
          }

          return true;
      }

      // this function will apply_hack,
      // if $test = true: just test all files and position
      // to check if there is no errors.
      // else will apply definitivly the hack
      // on error put string in $errors array at the
      // end test if any errors was found and return
      // true if no errors or false if some errors.
      function install_hack($hack_array, $test = false)
         {
            // globals var
            global $THIS_BASEPATH, $CURRENT_FOLDER, $dbhost, $dbuser, $dbpass, $database, $TABLE_PREFIX;

            $DEFAULT_ROOT = $THIS_BASEPATH;
            $DEFAULT_STYLE_PATH = "$THIS_BASEPATH/style/xbtit_default";
            $DEFAULT_LANGUAGE_PATH = "$THIS_BASEPATH/language/english";

            // reset errors array;
            unset($this->errors);
            $hacks = count($hack_array);
            // we have at least 1 hack listed
            if ($hacks)
              {
              for($i = 0; $i < $hacks; $i++)
                {
                $files = count($hack_array[$i]["file"]);
                // we have at least 1 file to touch
                if ($files)
                  {
                  for ($j = 0; $j < $files; $j++)
                    {
                    eval("\$this->file[\$j][\"name\"]=".$hack_array[$i]["file"][$j]["name"].";");
                    if (strtoupper($this->file[$j]["name"]) != "DATABASE")
                      {
                        $file_content=$this->open_read_file($this->file[$j]["name"], $j);
                        if (!$file_content)
                          continue; // file not found, we don't make other operations on this file...
                    }
                    $operations = count($hack_array[$i]["file"][$j]["operations"]);
                    // we have at least 1 operation to do on this file
                    if ($operations)
                      {
                      for ($k = 0; $k < $operations; $k++)
                        {
                        $action = str_replace("\"","",$hack_array[$i]["file"][$j]["operations"][$k]["action"]);
                        // what is the task?
                        unset($new_file_path);
                        unset($new_file_name);
                        $this->file[$j]["operation"] = ucfirst("$action");
                        switch($action)
                          {
                          case 'sql':
                              $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:green;\">OK</span>";

                              require_once(dirname(__FILE__)."/settings.php");
                              @mysql_connect($dbhost, $dbuser, $dbpass) or die("Error connecting to $dbhost!");
                              @mysql_select_db($database) or ($this->db_error());
                              // if we just test then that's all, else we will run the query
                              $thequery = str_replace("\"","\\\"", $hack_array[$i]["file"][$j]["operations"][$k]["data"]);
                              if (!$test)
                                 @mysql_query(str_replace("{\$db_prefix}", "$TABLE_PREFIX", $thequery));
                            break;

                          case 'copy':
                            eval("\$new_file_path=".$hack_array[$i]["file"][$j]["operations"][$k]["where"].";");
                            eval("\$new_file_name=".$hack_array[$i]["file"][$j]["operations"][$k]["data"].";");

                            // this part need to be done using ftp
                            $this->ftp_login_need = true;

                            $index=count($this->ftp_files_to_copy);
                            
                            $this->ftp_files_to_copy[$index]["index"] = $j;
                            $this->ftp_files_to_copy[$index]["origine"] = $this->file[$j]["name"];
                            $this->ftp_files_to_copy[$index]["newpath"] = $new_file_path;
                            $this->ftp_files_to_copy[$index]["newfile"] = $new_file_name;

                            break;

                          // when add or remove we must search
                          case 'add':
                          case 'remove':
                          case 'replace':
                            // for template files, comments must be html comment <!--- comment -->
                            if (substr(str_replace("\"","", $hack_array[$i]["file"][$j]["name"]),-4)==".tpl")
                              {
                              $begin_hack_str = "\n<!-- begin modification -->\n";
                              $begin_hack_str .= "<!-- hack: ".$hack_array[$i]["title"]." -->\n";
                              $begin_hack_str .= "<!-- operation #$k -->\n";
                              $end_hack_str = "\n<!-- end modification -->\n";
                              $end_hack_str .= "<!-- hack: ".$hack_array[$i]["title"]." -->\n";
                              $end_hack_str .= "<!-- operation #$k -->\n";
                            }
                            else
                              {
                              // we will put a comment before and after each change
                              $begin_hack_str = "\n// begin modification\n";
                              $begin_hack_str .= "// hack: ".$hack_array[$i]["title"]."\n";
                              $begin_hack_str .= "// operation #$k\n";
                              $end_hack_str = "\n// end modification\n";
                              $end_hack_str .= "// hack: ".$hack_array[$i]["title"]."\n";
                              $end_hack_str .= "// operation #$k\n";
                            }

                            $begin_hack_str ="";
                            $end_hack_str ="";

                            $string_to_search = str_replace("\r\n","\n", $hack_array[$i]["file"][$j]["operations"][$k]["search"]);
                            $file_content = str_replace("\r\n","\n", $file_content); // convert file containt into unix style
                            $pos = @strpos($file_content,$string_to_search);
                            // we find the position
                            if ($pos !== false)
                              {
                                if ($action == "add")
                                  {
                                    // we must find if before or after
                                    $where = str_replace("\"","", $hack_array[$i]["file"][$j]["operations"][$k]["where"]);
                                    $newpos = ($where == "before"?$pos:$pos+strlen($string_to_search));
                                    $file_content = substr($file_content,0,$newpos).$begin_hack_str.str_replace("\r\n","\n", $hack_array[$i]["file"][$j]["operations"][$k]["data"]).$end_hack_str.substr($file_content, $newpos);
                                }
                                elseif ($action == "replace")
                                  {
                                    $newpos = $pos + strlen($string_to_search);
                                    $file_content = substr($file_content,0,$pos).$begin_hack_str.str_replace("\r\n","\n", $hack_array[$i]["file"][$j]["operations"][$k]["data"]).$end_hack_str.substr($file_content, $newpos);
                                }
                                else
                                  { // we're removing...
                                    $newpos = $pos + strlen($string_to_search);
                                    if (substr(str_replace("\"","", $hack_array[$i]["file"][$j]["name"]),-4)==".tpl")
                                      $file_content = substr($file_content, 0, $pos).$begin_hack_str."<!-- *** REMOVED hack: ".$hack_array[$i]["title"]." operation #$k *** -->\n".substr($file_content,$newpos);
                                    else
                                      $file_content = substr($file_content, 0, $pos).$begin_hack_str."/*** REMOVED hack: ".$hack_array[$i]["title"]." operation #$k ***/\n".substr($file_content, $newpos);
                                }
                            }
                            else // we don't find the searched text
                              //$this->errors[]["message"]="Sorry <br />\n\"".nl2br(htmlspecialchars($string_to_search))."\"<br />\nto search was not found in file: ".$this->file[$j]["name"].".";
                              $this->_err_message("Sorry search string: \"".substr(nl2br(htmlspecialchars($string_to_search)),0,200)."...\" (first 20 chars) was not found)",$this->file[$j]["name"],"Ask Hack's Developer");
                            break;
                        } // end switch action
                      } // end for operations
                    } // end if operations
                    else
                      //$this->errors[]["message"]="Sorry no operations defined.";
                      $this->_err_message("Sorry no operations defined", $this->file[$j]["name"], "Ask Hack's Developer");
                                          
                    // it's not a test, we must save the current file
                    // we will make a new folder in hacks/backups/before_hack_name
                    // and we will copy files here
                    // then we will write the new file.
                    if ((strtoupper($this->file[$j]["name"]) != "DATABASE"))
                       {
                         if ($this->save_original($this->file[$j]["name"], $test));
                         // we have saved the originale
                           {
                           // we need to copy the file somewhere?
                           /* copy is now doing using ftp
                           if (!$test && isset($new_file_name) && isset($new_file_path))
                             {
                             // destination is writable
                             if ($this->file[$j]["status"] == "<span style=\"font-weight: bold; color:green;\">OK</span>")
                               {
                                if (@copy($this->file[$j]["name"], "$new_file_path/$new_file_name"))
                                  $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:green;\">OK</span>";
                                else
                                  {
                                    $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:red;\">Failed</span>";
                                    //$this->errors[]["message"] = "Error: copying ".$this->file[$j]["name"]." in new position $new_file_path/$new_file_name";
                                    $this->_err_message("Error copying file in $new_file_path/$new_file_name",$this->file[$j]["name"], "Check chmod+chown for $new_file_path");
                                }
                             }
                           }
                           else */
                           if ($action != "copy") // "normal" operation
                             {
                               if ($this->write_new_file($this->file[$j]["name"], $file_content, $j, $test))
                                 $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:green;\">OK</span>";
                               else
                                 $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:red;\">Failed</span>";
                           }
                         }
                    }
                    elseif ($action != "copy" && strtoupper($this->file[$j]["name"]) != "DATABASE")
                     {
                       if ($this->write_new_file($this->file[$j]["name"], $file_content, $j, true))
                         $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:green;\">OK</span>";
                       else
                         $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:red;\">Failed</span>";
                    }
                   // end test control :)

                  } // end for files    

                  // check for ftp need
                  if (!$this->ftp_session_begin())
                     return false;

                  // any files to chmod??
                  if (!$this->chmod_ftp_files())
                     return false;

                  // we try to copy new files using ftp
                  if (!$this->copy_ftp_files())
                     return false;

                  // was only test??? then we deleted files copied before.
                  if ($test)
                     @$this->ftp_fdelete($this->ftp_files_to_copy);

                  $this->ftp_session_end();

                  $this->save_original("", $test, true);

                } // end if files
                else
                  //$this->errors[]["message"]="Sorry no files defined.";
                  $this->_err_message("Sorry no files defined", "None", "Ask Hack's Developer");

              } // end for hacks
            } //end if hacks
            else
              //$this->errors[]["message"]="Sorry no hack defined.";
              $this->_err_message("Sorry no hack defined", "None", "Ask Hack's Developer");

            // ok, we've do nothing (in test mode) but seems ok.
            // finally we check if all was gone as should
            if (isset($this->errors))
              if (count($this->errors)>0)
                  return false;
              else
                  return true;    
            else
              return true;
      }

      // this function will remove hack,
      // if $test = true: just test all files and position
      // to check if there is no errors.
      // else will remove definitivly the hack
      // on error put string in $errors array at the
      // end test if any errors was found and return
      // true if no errors or false if some errors.
      function uninstall_hack($hack_array,$test=false)
         {
            // globals var
            global $THIS_BASEPATH, $CURRENT_FOLDER, $dbhost, $dbuser, $dbpass, $database;

            $DEFAULT_ROOT = $THIS_BASEPATH;
            $DEFAULT_STYLE_PATH = "$THIS_BASEPATH/style/xbtit_default";
            $DEFAULT_LANGUAGE_PATH = "$THIS_BASEPATH/language/english";

            // reset errors array;
            unset($this->errors);
            $hacks = count($hack_array);
            // we have at least 1 hack listed
            if ($hacks)
              {
              for($i = 0;$i < $hacks; $i++)
                {
                $files = count($hack_array[$i]["file"]);
                // we have at least 1 file to touch
                if ($files)
                  {
                  for ($j = 0; $j < $files; $j++)
                    {
                    eval("\$this->file[\$j][\"name\"]=".$hack_array[$i]["file"][$j]["name"].";");
                    if (strtoupper($this->file[$j]["name"]) != "DATABASE")
                      {
                        $file_content = $this->open_read_file($this->file[$j]["name"],$j);
                        if (!$file_content)
                          continue; // file not found, we don't make other operations on this file...
                    }
                    $operations = count($hack_array[$i]["file"][$j]["operations"]);
                    // we have at least 1 operation to do on this file
                    if ($operations)
                      {
                      for ($k = 0;$k < $operations; $k++)
                        {
                        $action = str_replace("\"", "", $hack_array[$i]["file"][$j]["operations"][$k]["action"]);
                        // what is the task?
                        unset($new_file_path);
                        unset($new_file_name);
                        switch($action)
                          {
                          case 'sql':
                              // on uninstall we will do nothing (don't remove the fields)
                              $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:green;\">OK</span>";
                            break;

                          case 'copy':
                              // atm we do nothing, dunno if we must remove added files...?
                              $this->file[$j]["status"]="<span style=\"font-weight: bold; color:green;\">OK</span>";
                            break;

                          // when add or remove we must search
                          case 'add':
                          case 'remove':
                          case 'replace':
                            // for template files, comments must be html comment <!--- comment -->
                            if (substr(str_replace("\"", "", $hack_array[$i]["file"][$j]["name"]),-4) == ".tpl")
                              {
                              $begin_hack_str = "\n<!-- begin modification -->\n";
                              $begin_hack_str .= "<!-- hack: ".$hack_array[$i]["title"]." -->\n";
                              $begin_hack_str .= "<!-- operation #$k -->\n";
                              $end_hack_str = "\n<!-- end modification -->\n";
                              $end_hack_str .= "<!-- hack: ".$hack_array[$i]["title"]." -->\n";
                              $end_hack_str .= "<!-- operation #$k -->\n";
                              // removing the hack, so we search what has be added or replacement
                              $string_to_search = $begin_hack_str.($action == "remove"?"<!-- *** REMOVED *** -->\n":str_replace("\r\n", "\n", $hack_array[$i]["file"][$j]["operations"][$k]["data"]).$end_hack_str);
                              $string_to_search_1 = ($action == "remove"?"<!-- *** REMOVED hack: ".$hack_array[$i]["title"]." operation #$k *** -->\n":str_replace("\r\n", "\n", $hack_array[$i]["file"][$j]["operations"][$k]["data"]));
                            }
                            else
                              {
                              $begin_hack_str = "\n// begin modification\n";
                              $begin_hack_str .= "// hack: ".$hack_array[$i]["title"]."\n";
                              $begin_hack_str .= "// operation #$k\n";
                              $end_hack_str = "\n// end modification\n";
                              $end_hack_str .= "// hack: ".$hack_array[$i]["title"]."\n";
                              $end_hack_str .= "// operation #$k\n";
                              // removing the hack, so we search what has be added or replacement
                              $string_to_search = $begin_hack_str.($action == "remove"?"// *** REMOVED ***\n":str_replace("\r\n", "\n", $hack_array[$i]["file"][$j]["operations"][$k]["data"]).$end_hack_str);
                              $string_to_search_1 = ($action == "remove"?"/*** REMOVED hack: ".$hack_array[$i]["title"]." operation #$k ***/\n":str_replace("\r\n", "\n", $hack_array[$i]["file"][$j]["operations"][$k]["data"]));
                            }
                            $file_content = str_replace("\r\n", "\n", $file_content); // convert file containt into unix style
                            $pos = @strpos($file_content, $string_to_search);

                            // not found position using "old" method, let us try with new one (without comments!)
                            if ($pos === false)
                               {
                                 $string_to_search = $string_to_search_1;
                                 $pos = @strpos($file_content, $string_to_search);
                             }


                            // we find the position
                            if ($pos !== false)
                              {
                                $newpos = $pos + strlen($string_to_search);
                                if ($action == "add")
                                  {
                                  // then when uninstalling we remove ;)
                                    $file_content = substr($file_content, 0, $pos).substr($file_content, $newpos);
                                }
                                elseif ($action == "replace")
                                  {
                                  // we replace the replacement with original
                                    $file_content = substr($file_content, 0, $pos).str_replace("\r\n","\n", $hack_array[$i]["file"][$j]["operations"][$k]["search"]).substr($file_content, $newpos);
                                }
                                else
                                  { // we put back again the removed string
                                    $file_content = substr($file_content, 0, $pos).str_replace("\r\n", "\n", $hack_array[$i]["file"][$j]["operations"][$k]["search"]).substr($file_content, $newpos);
                                }
                            }
                            else // we don't find the searched text
                              //$this->errors[]["message"]="Sorry <br />\n\"".nl2br(htmlspecialchars($string_to_search))."\"<br />\nto search was not found in file: ".$this->file[$j]["name"].".";
                              $this->_err_message("Sorry search string: \"".substr(nl2br(htmlspecialchars($string_to_search)), 0, 200)."...\" (first 20 chars) was not found)", $this->file[$j]["name"], "Ask Hack's Developer");
                            break;
                        } // end switch action
                      } // end for operations
                    } // end if operations
                    else
                      //$this->errors[]["message"]="Sorry no operations defined.";
                      $this->_err_message("Sorry no operations defined", "None", "Ask Hack's Developer");

                    // from this point all operations are the same as when we have installed the hack...
                    
                    // it's not a test, we must save the current file
                    // we will make a new folder in hacks/backups/before_hack_name
                    // and we will copy files here
                    // then we will write the new file.
                    if ((strtoupper($this->file[$j]["name"]) != "DATABASE"))
                       {
                         if ($this->save_original($this->file[$j]["name"], $test));
                         // we have saved the originale
                           {
                           // file was copied somewhere?
                           if (!$test && isset($new_file_name) && isset($new_file_path))
                             {
                             // destination is writable
                             if ($this->file[$j]["status"] == "<span style=\"font-weight: bold; color:green;\">OK</span>")
                               {
                                if (@unlink("$new_file_path/$new_file_name"))
                                  $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:green;\">OK</span>";
                                else
                                  {
                                    $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:red;\">Failed</span>";
                                    //$this->errors[]["message"]="Error: copying ".$this->file[$j]["name"]." in new position $new_file_path/$new_file_name";
                                    $this->_err_message("Error deleting file in $new_file_path/$new_file_name", $this->file[$j]["name"],"Check chmod+chown for $new_file_path");
                                }
                             }
                           }
                           elseif ($action != "copy")  // "normal" operation
                             {
                               if ($this->write_new_file($this->file[$j]["name"], $file_content, $j, $test))
                                 $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:green;\">OK</span>";
                               else
                                 $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:red;\">Failed</span>";
                           }
                         }
                    }
                    elseif ($action != "copy" && strtoupper($this->file[$j]["name"]) != "DATABASE")
                     {
                       if ($this->write_new_file($this->file[$j]["name"], $file_content, $j, true))
                         $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:green;\">OK</span>";
                       else
                         $this->file[$j]["status"] = "<span style=\"font-weight: bold; color:red;\">Failed</span>";
                    }
                   // end test control :)


                  } // end for files    
                  
                  if (!$this->ftp_session_begin())
                     return false;

                  // any files to chmod??
                  if (!$this->chmod_ftp_files())
                     return false;

                  $this->ftp_session_end();

                  $this->save_original("", $test, true);

                } // end if files
                else
                  //$this->errors[]["message"] = "Sorry no files defined.";
                  $this->_err_message("Sorry no files defined", "None", "Ask Hack's Developer");
              } // end for hacks
            } //end if hacks
            else
              //$this->errors[]["message"] = "Sorry no hack defined.";
              $this->_err_message("Sorry no hack defined", "None", "Ask Hack's Developer");
            // ok, we've do nothing but seems ok.
            // finally we check if all was gone as should
            if (isset($this->errors))
              if (($this->errors) > 0)
                  return false;
              else
                  return true;    
            else
              return true;
      }

      // private
      // will write the input $new_content in $file_with_path
      // return false in case of error
      function write_new_file($file_with_path, $new_content, $ind, $for_test=false)
        {

        // globals var
        global $THIS_BASEPATH, $CURRENT_FOLDER;

        // is the file writable?
        if (!is_writable($file_with_path))
          {
          @chmod($file_with_path, 0777);
          if (!is_writable($file_with_path))
            {
              //$this->errors[]["message"]="unable to write new content in $file_with_path!";
              //$this->_err_message("Unable to write new content in file",$file_with_path,"Check chmod+chown for $file_with_path");
              //return false;
              // we will do this via ftp
              $this->ftp_login_need = true;
              $index=count($this->ftp_files_to_chmod);
              $this->ftp_files_to_chmod[$index]["index"] = $ind;
              $this->ftp_files_to_chmod[$index]["file"] = $file_with_path;
              $this->ftp_files_to_chmod[$index]["mode"] = "777";

          }
        }
        // it's only for testing, but seems to be writable
        if ($for_test)
          return true;
        // we will write the new content in the truncated file
        $fp = fopen($file_with_path, "w");
        if (!$fp)
          {
            //$this->errors[]["message"]="unable to open $file_with_path!";
            $this->_err_message("Unable to open file", $file_with_path, "Check chmod+chown for $file_with_path");
            return false;
        }
        if (fwrite($fp, $new_content) === false)
          {
            //$this->errors[]["message"]="unable to write new content in $file_with_path!";
            $this->_err_message("Unable to write new content in file", $file_with_path, "Check chmod+chown for $file_with_path");
            @fclose($fp);
            return false;
        }
        @fclose($fp);
        @chmod($file_with_path, 0755);
        @chown($file_with_path, "root");
        return true;
      }

      // private
      // will save the input $file_with_path in a new
      // create folder called "backup" in folder which the
      // script has origin, or use the already existing one
      function save_original($file_with_path, $in_test = false, $pack = false)
        {

        // globals var
        global $THIS_BASEPATH, $CURRENT_FOLDER;

        // just in case is not set....
        if ($this->hack_path == "")
          $this->hack_path = "$THIS_BASEPATH/hacks";

        if (!is_writable($this->hack_path))
          {
          @chmod($this->hack_path, 0777);
          if (!is_writable($this->hack_path))
            {
              $this->ftp_login_need = true;
              $new = true;
              foreach($this->ftp_files_to_chmod as $ftcm)
                {
                 if (in_array($this->hack_path, $ftcm))
                   $new = false;
              }
              if ($new)
                {
                  $index = count($this->ftp_files_to_chmod);
                  $this->ftp_files_to_chmod[$index]["file"] = $this->hack_path;
                  $this->ftp_files_to_chmod[$index]["mode"] = "777";
              }
              return false;
          }
        }

        if ($in_test)
           return true;

        if (!$pack)
          {
           $this->files_to_backup[] = $file_with_path;
           return true;
        }


        // no files to backup???
        if (count($this->files_to_backup) == 0)
           return true;

        $archive = new tar_file($this->hack_path."/backup-".date("d-m-Y_H-i-s").".tar");
        $archive->set_options(array("basedir" => "$THIS_BASEPATH"));
        foreach ($this->files_to_backup as $ftb)
           $archive->add_files($ftb);
        $archive->create_archive();

        if (count($archive->errors) > 0)
          {
          $this->_err_message("Unable to write in $this->hack_path", "Folder", "Check chmod+chown for $this->hack_path");
          return false;
        }

        return true;
        
  }

}

?>
